(function() {
    // Uploads a 2x2 floating-point texture where one pixel is 2 and the other
    // three pixels are 0. Linear filtering is only supported if a sample taken
    // from the center of that texture is (2 + 0 + 0 + 0) / 4 = 0.5.
    function supportsOESTextureFloatLinear(gl) {
      // Need floating point textures in the first place
      if (!gl.getExtension('OES_texture_float')) {
        return false;
      }
  
      // Create a render target
      var framebuffer = gl.createFramebuffer();
      var byteTexture = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, byteTexture);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
      gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, byteTexture, 0);
  
      // Create a simple floating-point texture with value of 0.5 in the center
      var rgba = [
        2, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 0
      ];
      var floatTexture = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, floatTexture);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.FLOAT, new Float32Array(rgba));
  
      // Create the test shader
      var program = gl.createProgram();
      var vertexShader = gl.createShader(gl.VERTEX_SHADER);
      var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
      gl.shaderSource(vertexShader, '\
        attribute vec2 vertex;\
        void main() {\
          gl_Position = vec4(vertex, 0.0, 1.0);\
        }\
      ');
      gl.shaderSource(fragmentShader, '\
        uniform sampler2D texture;\
        void main() {\
          gl_FragColor = texture2D(texture, vec2(0.5));\
        }\
      ');
      gl.compileShader(vertexShader);
      gl.compileShader(fragmentShader);
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      gl.linkProgram(program);
  
      // Create a buffer containing a single point
      var buffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0]), gl.STREAM_DRAW);
      gl.enableVertexAttribArray(0);
      gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
  
      // Render the point and read back the rendered pixel
      var pixel = new Uint8Array(4);
      gl.useProgram(program);
      gl.viewport(0, 0, 1, 1);
      gl.bindTexture(gl.TEXTURE_2D, floatTexture);
      gl.drawArrays(gl.POINTS, 0, 1);
      gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  
      // The center sample will only have a value of 0.5 if linear filtering works
      return pixel[0] === 127 || pixel[0] === 128;
    }
  
    // The constructor for the returned extension object
    function OESTextureFloatLinear() {
    }
  
    // Cache the extension so it's specific to each context like extensions should be
    function getOESTextureFloatLinear(gl) {
      if (gl.$OES_texture_float_linear$ === void 0) {
        Object.defineProperty(gl, '$OES_texture_float_linear$', {
          enumerable: false,
          configurable: false,
          writable: false,
          value: new OESTextureFloatLinear()
        });
      }
      return gl.$OES_texture_float_linear$;
    }
  
    // This replaces the real getExtension()
    function getExtension(name) {
      return name === 'OES_texture_float_linear'
        ? getOESTextureFloatLinear(this)
        : oldGetExtension.call(this, name);
    }
  
    // This replaces the real getSupportedExtensions()
    function getSupportedExtensions() {
      var extensions = oldGetSupportedExtensions.call(this);
      if (extensions.indexOf('OES_texture_float_linear') === -1) {
        extensions.push('OES_texture_float_linear');
      }
      return extensions;
    }
  
    // Get a WebGL context
    try {
      var gl = document.createElement('canvas').getContext('experimental-webgl');
    } catch (e) {
    }
  
    // Don't install the polyfill if the browser already supports it or doesn't have WebGL
    if (!gl || gl.getSupportedExtensions().indexOf('OES_texture_float_linear') !== -1) {
      return;
    }
  
    // Install the polyfill if linear filtering works with floating-point textures
    if (supportsOESTextureFloatLinear(gl)) {
      var oldGetExtension = WebGLRenderingContext.prototype.getExtension;
      var oldGetSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions;
      WebGLRenderingContext.prototype.getExtension = getExtension;
      WebGLRenderingContext.prototype.getSupportedExtensions = getSupportedExtensions;
    }
  }());
  